package base.core.log;

import base.core.util.Dates;
import base.core.util.Utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.time.Duration;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;

/**
 * 打印系统日志
 */
@Slf4j(topic = "com.elegant.log.api")
@Aspect
@Component
@ConditionalOnProperty(name = "logging.api-log",havingValue = "true")
public class LogAspect {

    /**
     * 打印接口日志
     */
    @Pointcut(value = "execution(* base.core.spring.BaseController.*(..))||@within(org.springframework.web.bind.annotation.RestController)")
    public void api() {
    }

    @Before(value = "api()")
    public void beforeApi() {
        Mark.start("接口请求开始", null);
    }

    @After(value = "api()")
    public void afterApi(JoinPoint joinPoint) throws JsonProcessingException {
        Mark.mark("接口请求结束", null);
        printLog();
    }

    private void printLog() throws JsonProcessingException {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        Map<String, String[]> parameterMap = request.getParameterMap();
        String parameters = new ObjectMapper().writeValueAsString(parameterMap).replaceAll("[\\[\\]]", "");
        List<Mark.Point> pointList = Mark.marks();
        Mark.Point maxDuration = pointList.stream().max(Comparator.comparing(Mark.Point::getDuration)).orElse(new Mark.Point());

        StringBuffer buffer = new StringBuffer();
        buffer.append("\n");
        buffer.append("╔═════════════════════════════════════════════════════════════════").append("\n");
        buffer.append(Utils.format("    {} 发起 {} 请求: {} ", getIP(request), request.getMethod(), request.getRequestURI())).append("\n");
        buffer.append(Utils.format("    参数: {} ", parameters)).append("\n");
        buffer.append(Utils.format("    接口执行详情: ")).append("\n");
        buffer.append(Utils.format("    ==============================================")).append("\n");
        buffer.append(Utils.format("        本次统计耗时: {} 毫秒", Duration.between(pointList.get(0).getMarkTime(), pointList.get(pointList.size() - 1).getMarkTime()).toMillis())).append("\n");
        buffer.append(Utils.format("        最大耗时操作: {} ; 共计: {} 毫秒: ", maxDuration.getTitle(), maxDuration.getDuration())).append("\n");
        buffer.append(Utils.format("        start  time : {}", Dates.asString(pointList.get(0).getMarkTime(), "yyyy-MM-dd HH:mm:ss.SSS"))).append("\n");
        buffer.append(Utils.format("        latest time : {}", Dates.asString(pointList.get(pointList.size() - 1).getMarkTime(), "yyyy-MM-dd HH:mm:ss.SSS"))).append("\n");
        buffer.append(Utils.format("        接口执行详情: ")).append("\n");

        for (Mark.Point point : pointList) {
            buffer.append(Utils.format("        Mark: {} ; 阶段耗时: {} 毫秒; 内容: {}", point.getTitle(), point.getDuration(), point.getContent())).append("\n");
        }
        buffer.append("╚═════════════════════════════════════════════════════════════════");
        buffer.append("\n");
        log.debug(buffer.toString());
    }

    public static String getIP(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (!checkIP(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (!checkIP(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (!checkIP(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    private static boolean checkIP(String ip) {
        if (ip == null || ip.length() == 0 || "unkown".equalsIgnoreCase(ip)
                || ip.split(".").length != 4) {
            return false;
        }
        return true;
    }

}